#ifndef TREEVIEW_H
#define TREEVIEW_H

#include <vector>
#include <string>
#include <iostream>
#include <map>

using namespace std; // remove: who uses this header file shouldn't have his naming affected

// - All provisonal (until I understand why I cannot set it as a static member)
extern int C; 
extern bool dNAP;
// ----------------------------------------------------------------------------


class TreeView;

struct EPoint
{
	double e;
	double p;
	int c;

	const EPoint* left;
	const EPoint* right;

	TreeView* tv;

	EPoint(double, double, int, const EPoint*, const EPoint*, TreeView*);

	void get_investments(map<string,int>& out) const;
};

inline bool operator< (const EPoint& ep1, const EPoint& ep2)
{
	return ep1.e < ep2.e;
}

inline ostream& operator<< (ostream& out, const EPoint& ep)
{
	out << "(" << ep.e << "," << ep.p << ")";
	return out;
}

inline double non_right_turn(const EPoint& ep0, const EPoint& ep1, const EPoint& ep2)
{
	// Calculated with the vectorial (or 'cross') product (CLRS pag.936):

	return (ep1.e - ep0.e)*(ep2.p - ep0.p) - (ep2.e - ep0.e)*(ep1.p - ep0.p); //>= 0;
}

// This class will need to be divided in two classes: a base class with all the 
// reusable code, and a derived class with all the code dependent on cats.
//
class TreeView
{
	// --------- Structural ---------
	TreeView* next;     // never null
	TreeView* rev;      // never null
	double length;
	string name;        // only makes sense for tips
	string repr;
	// ------------------------------
	//
	// No need for a 'bool tip'... see is_tip().

	vector<double>* pT; // only makes sense for tips
	typedef vector<EPoint> EPlist;
	vector<EPlist> eplist;

	// -------- Constructors --------
	TreeView(TreeView* next, TreeView* rev, double length = 0);
	// ------------------------------
	void init_structure(istream& in);
	
public:

	// -------- Constructors --------
	TreeView(const string& treefile);
	TreeView(istream& tree_in);
	// ------------------------------

	// static int C;
	// static bool dNAP;

	int cost; // Only makes sense for tips and is defined as the least c
	          // for which (*pT)[c] is maximised.
	          // It also represents the maximum expenditure for this tip.

	// ------ Member functions ------
	inline TreeView& get_rev()                      { return *rev; }
	inline double    get_length()                   { return length; }
	inline string&   get_name()                     { return name; }
	inline bool      is_tip()                       { return rev == rev->next; }
	//inline void    set_name(string new_name)      { name = new_name; }
	//inline void    set_pT(vector<double>* new_pT) { pT = new_pT; }
	inline void      reset_pT()    { delete pT; pT = new vector<double>(C+1, 0); cost = C+1;}


	void bottom_up(void (TreeView::*calc)());
	void top_down (void (TreeView::*calc)());

	void resolve_monofurcation();
	void resolve_multifurcation();
	void calc_eplists();

	void calc_representation();
	void print_tree(ostream& out, bool semicolon = false);

	vector<TreeView*>  childs();
	vector<TreeView*> parents();
	// alternatively, define them with the same interface as:

	int get_tips(vector<TreeView*>& out);

	void set_length(istream& in); // looks for ':' and sets length

	// --- specific for cats ---
	void set_cost_benefits(const string& costfile);
	void set_cost_benefits(istream& in);
	void set_cost_benefits(map<string,vector<double>*>& cb);
	void set_cost_benefit (map<string,vector<double>*>& cb);

	void combine_into_eplist(int c, const EPlist& epl_L, const EPlist& epl_R);
	void print_eplists(char* prefix);
	void print_eplists(ostream& out);
	void clean_eplist(int c);
	EPoint* get_best_epoint(int c);

	double transform_lengths_and_probs();

	void print_rPD_solutions_as_table(ostream& out);
	void print_uPD_solutions_as_table(ostream& out);
	int set_header_of_output_table(ostream& out, vector<string>& taxon_names);

	// Old output functions (now disused):
	void print_all_solutions_for_rooted_PD(ostream& out);
	void print_solution_for_rooted_PD  (int budget, ostream& out = cout);
	void print_all_solutions_for_unrooted_PD(ostream& out);
	void print_solution_for_unrooted_PD(int budget, ostream& out = cout);
	void print_expenditures(ostream& out, EPoint* ep, int root_cost = 0);
	EPoint* reduced_cost_version(EPoint* ep);
	// ------------------------------
};

#endif // TREEVIEW_H
